package com.docshell.service;

import java.util.ArrayList;
import java.util.HashMap;

import com.docshell.common.DbManager;
import com.docshell.common.DisplayManager;
import com.docshell.common.SessionManager;
import com.docshell.constants.DocShellConstants;
import com.docshell.exceptions.DocShellException;
import com.documentum.fc.common.DfException;

public class CommandHandler {

	class APICommandDetails {
		int NumLines;
		String Prompt;
		int ApiType;
		int NoOfArguments;
		
		APICommandDetails(int a_NumLines, String a_Prompt, int a_ApiType, int a_NoOfArguments) {
			NumLines = a_NumLines;
			this.Prompt = a_Prompt;
			this.ApiType = a_ApiType;
			this.NoOfArguments = a_NoOfArguments;
		}
	}
	
	class InternalCommandDetails {
		int NumLines;
		String Prompt;
		int NoOfArguments;
		String HelpText;
		String Syntax;

		InternalCommandDetails(int a_NumLines, String a_Prompt, int a_NoOfArguments, String a_HelpText, String a_Syntax) {
			this.NumLines = a_NumLines;
			this.Prompt = a_Prompt;
			this.NoOfArguments = a_NoOfArguments;
			this.HelpText = a_HelpText;
			this.Syntax = a_Syntax;
		}		
	}
	
	private String m_CurrentFolder = "";
	private String m_StartupFolder = "";
	private int m_CommandType = 0;
	private int m_NumOfLines;
	
	private ArrayList m_CommandLine = new ArrayList();
	private ArrayList m_PrevCommandLine = new ArrayList();
	
	private HashMap apiCommands = new HashMap();;
	private HashMap intCommands = new HashMap();
	
	public CommandHandler() {
		
	}

	public void initialize(String a_startupFolder) throws DocShellException {
		
		DisplayManager.writeLine("Loading ...");
		
		APICommandDetails apiCmdDtls = new APICommandDetails(1, null, DocShellConstants.GET_API_TYPE, 2);
		apiCommands.put("dump", apiCmdDtls);
		apiCmdDtls = new APICommandDetails(2, "Value", DocShellConstants.SET_API_TYPE, 3);
		apiCommands.put("set", apiCmdDtls);
		apiCmdDtls = new APICommandDetails(1, null, DocShellConstants.GET_API_TYPE, 3);
		apiCommands.put("get", apiCmdDtls);
		apiCmdDtls = new APICommandDetails(1, null, DocShellConstants.EXEC_API_TYPE, 2);
		apiCommands.put("save", apiCmdDtls);
		apiCmdDtls = new APICommandDetails(1, null, DocShellConstants.GET_API_TYPE, 3);
		apiCommands.put("fetch", apiCmdDtls);
		apiCmdDtls = new APICommandDetails(1, null, DocShellConstants.GET_API_TYPE, 3);
		apiCommands.put("retrieve", apiCmdDtls);
		apiCmdDtls = new APICommandDetails(1, null, DocShellConstants.EXEC_API_TYPE, 2);
		apiCommands.put("lock", apiCmdDtls);
		apiCmdDtls = new APICommandDetails(1, null, DocShellConstants.EXEC_API_TYPE, 2);
		apiCommands.put("unlock", apiCmdDtls);
		apiCmdDtls = new APICommandDetails(1, null, DocShellConstants.EXEC_API_TYPE, 3);
		apiCommands.put("trace", apiCmdDtls);
		
		InternalCommandDetails intCmdDtls = new InternalCommandDetails(1,null,2,"Changes Current Directory", "cd - Changes to Home Cabinet\ncd .. - Changes to parent directory\ncd \\ - Changes to root directory\ncd dir_name - Changes to specified directory");
		intCommands.put("cd", intCmdDtls);
		intCmdDtls = new InternalCommandDetails(1,null,2,"Prints List of supported commands or a given command", "help - Prints list of available command\nhelp command name - Prints help of given command");
		intCommands.put("help", intCmdDtls);
		intCmdDtls = new InternalCommandDetails(1,null,1,"Exit DocShell", "Exit DocShell");
		intCommands.put("exit", intCmdDtls);
		intCmdDtls = new InternalCommandDetails(1,null,1,"Exit DocShell", "Exit DocShell");
		intCommands.put("quit", intCmdDtls);
		intCmdDtls = new InternalCommandDetails(1,null,4,"Connects to Docbase", "");
		intCommands.put("connect", intCmdDtls);
		intCmdDtls = new InternalCommandDetails(1,null,1,"Disconnects from Docbase", "");
		intCommands.put("disconnect", intCmdDtls);
		intCmdDtls = new InternalCommandDetails(1,null,3,"Displays objects of a folder", "ls - Display objects of current folder\nls -l - Displays additional information\nls folder_path - display content of specified folder path");
		intCommands.put("ls", intCmdDtls);
		intCmdDtls = new InternalCommandDetails(1,null,1,"Displays current folder", "pwd - Display Displays current folder");
		intCommands.put("pwd", intCmdDtls);
		intCmdDtls = new InternalCommandDetails(1,null,2,"Creates a folder", "md - Creates a folder under current folder");
		intCommands.put("md", intCmdDtls);
		intCmdDtls = new InternalCommandDetails(1,null,2,"Removes a folder", "rm - Removes a folder//object");
		intCommands.put("rm", intCmdDtls);		
		intCmdDtls = new InternalCommandDetails(1,null,2,"Checks out a document object", "co - document object name");
		intCommands.put("co", intCmdDtls);		
		intCmdDtls = new InternalCommandDetails(1,null,2,"Check in a document object", "ci - document object name");
		intCommands.put("ci", intCmdDtls);		

		setStartupFolder(a_startupFolder);
		
		try {
			if (SessionManager.getDocbase()!=null && SessionManager.getUserName()!=null && SessionManager.getPassword()!=null) {
				DisplayManager.writeLine("Connecting to Docbase...");
				SessionManager.initialize();
				if (SessionManager.getIsConnected()) {
					DisplayManager.writeLine("Connected to " + SessionManager.getDocbase());
					if (null==getStartupFolder() || getStartupFolder().equals(DocShellConstants.EMPTY_STRING)) {
						setStartupFolder(DbManager.getUserHomeCabinet());
					}
					else
					{
						if(!DbManager.checkFolderPath(getStartupFolder())) {
							setStartupFolder("/");
						}
					}
					setCurrentFolder(getStartupFolder());
					DisplayManager.setPrompt(getCurrentFolder());
				}
			}	
			else {
				setCurrentFolder("/");
				setStartupFolder("/");
				DisplayManager.setPrompt(getCurrentFolder());
			}
		}
		catch (DfException dfe) {
			DisplayManager.writeLine("Error during startup...");
			DisplayManager.writeLine(dfe.getMessage());
		}
		catch (DocShellException dse) {
			DisplayManager.writeLine(dse.getMessage());
			DisplayManager.writeLine(dse.getMessage().toString());
		}
	}
	
	public void setCurrentFolder(String m_CurrentFolder) {
		if (m_CurrentFolder.substring(0,1).equals("/")) {
			this.m_CurrentFolder = m_CurrentFolder ;
		}
		else {
			this.m_CurrentFolder = "/" + m_CurrentFolder;
		}
		DisplayManager.setPrompt(m_CurrentFolder);
	}

	public String getCurrentFolder() {
		return this.m_CurrentFolder;
	}

	public void setStartupFolder(String m_StartupFolder) {
		if ((null!=m_StartupFolder) && (!m_StartupFolder.equals(DocShellConstants.EMPTY_STRING))) {
			if (m_StartupFolder.substring(0,1).equals("/")) {
				this.m_StartupFolder = m_StartupFolder;
			}
			else
			{
				this.m_StartupFolder = "/" + m_StartupFolder;
			}
		}
	}

	public String getStartupFolder() {
		return this.m_StartupFolder;
	}
	
	public void execute() throws DocShellException {
		try {
			if (m_CommandLine.size()==0) {
				m_CommandLine = m_PrevCommandLine;
			}
			if (m_CommandType == DocShellConstants.INTERNAL_COMMAND) {
				intCommandHandler();
			}
			else if  (m_CommandType == DocShellConstants.API_COMMAND) {
				apiCommandHandler();
			}
			else if  (m_CommandType == DocShellConstants.DQL_COMMAND) {
				dqlCommandHandler();
			}		
			else {
				displayError("Unknown command type!");
			}
			m_PrevCommandLine = m_CommandLine;
			m_CommandLine.clear();
			
			resetPrompt();
		}
		catch (Exception ex) {
			throw new DocShellException(DocShellException.UNKNOWN_EXCEPTION, ex.toString());
		}
	}

	private void dqlCommandHandler() throws DocShellException {
		try {
			String commandLine = "";
			for (int counter=0;counter<m_CommandLine.size();counter++) {
				commandLine = (commandLine + " " + (String) m_CommandLine.get(counter)).trim();
			}
			String [] commandArray = commandLine.split(" ");
			String command = commandArray[0];
			QueryHandler qryHandler = new QueryHandler();
			if (command.equalsIgnoreCase("select")) {
				qryHandler.readQuery(commandLine);
			}
			else {
				qryHandler.execQuery(commandLine);
			}
		}
		catch (DfException dfe) {
			DisplayManager.writeLine(DocShellException.ERROR_IN_DQL);
			DisplayManager.writeLine(dfe.getMessage());
		}
	}

	private void apiCommandHandler() throws DocShellException {
		try {
			String commandLine = (String) m_CommandLine.get(0);
			String [] commandArray = commandLine.split("\\s*,\\s*");
			String command = commandArray[0];
			String arguments = "";
			
			for (int counter=1;counter<commandArray.length;counter++) {
				arguments = arguments + "," + commandArray[counter];
			}
			
			arguments = arguments.length() > 1 ? arguments.substring(1) : "";
			
			APICommandDetails apiCmd = (APICommandDetails) apiCommands.get(command);
	
			if (apiCmd.NoOfArguments==commandArray.length) {
				ApiHandler apiHandler = new ApiHandler();
				apiHandler.setCommand(command);
				apiHandler.setArguments(arguments);
				apiHandler.setApiType(apiCmd.ApiType);
				if (apiCmd.NumLines==2) {
					String strValue  = (String) m_CommandLine.get(1);
					apiHandler.setValue(strValue);
				}
				apiHandler.execAPI();
			}
			else
			{
				displayError("Invalid numbder of arguments passed to " + command + " command.");
			}
		}
		catch (DfException dfe) {
			DisplayManager.writeLine(DocShellException.ERROR_IN_API);
			DisplayManager.writeLine(dfe.getMessage());
		}
	}

	private void intCommandHandler() throws DocShellException {
		try {
			String commandLine = (String) m_CommandLine.get(0);
			String [] commandArray = commandLine.split("\\s*,\\s*");
			String command = commandArray[0];
			if (command.equals("cd")) {
				if (commandArray.length==1) {
					setCurrentFolder(getStartupFolder());
				}
				else if (commandArray.length==2) {
					String lsNewPath = folderPathParser(commandArray[1]);
					if (DbManager.checkFolderPath(lsNewPath)) {
						setCurrentFolder(lsNewPath);
					} else {
						String errMsg = "Not able to locate " + commandArray[1] + " folder, Please check path of the folder!";
						displayError(errMsg);
					}
				}
				else {
					displayCommandError(DocShellConstants.INVALID_CD_ARGUMENTS);

				}
			}
			else if (command.equals("md")) { 
				if (commandArray.length==2) {
					String lsNewPath = folderPathParser(commandArray[1]);
					
					if (DbManager.checkFolderPath(lsNewPath)) {
						String errMsg = "Folder " + commandArray[1] + " already exists!";
						displayError(errMsg);
					} else {
						int newFolderNamePosition = lsNewPath.lastIndexOf('/');
						String lsTargetFolder = null;
						String lsTargetPath = null;
						lsTargetFolder = lsNewPath.substring(newFolderNamePosition+1);
						lsTargetPath = lsNewPath.substring(0,newFolderNamePosition);
						
						if (DbManager.checkFolderPermission(lsTargetPath, DocShellConstants.WRITE_PERMISSION)) {
							if (DbManager.createFolder(lsTargetFolder, lsTargetPath)) {
								String errMsg = "Folder " + lsTargetFolder + " created successfully!";
								displayError(errMsg);								
							}
							else {
								String errMsg = "Folder " + lsTargetFolder + " could not be created!";
								displayError(errMsg);
							}
						}
						else
						{
							String errMsg = "No write permission in folder " + commandArray[1] + " folder, Please check path of the folder!";
							displayError(errMsg);
						}
					}
				}
				else {
					displayCommandError(DocShellConstants.INVALID_MD_ARGUMENTS);

				}
			}
			else if (command.equals("rm")) { 
				if (commandArray.length==2) {
					String lsNewPath = folderPathParser(commandArray[1]);
					
					if (!DbManager.checkFolderPath(lsNewPath)) {
						String errMsg = "Folder//Document " + commandArray[1] + " does not exist!";
						displayError(errMsg);
					} else {
						int newFolderNamePosition = lsNewPath.lastIndexOf('/');
						String lsTargetObject = null;
						String lsTargetPath = null;
						lsTargetObject = lsNewPath.substring(newFolderNamePosition+1);
						lsTargetPath = lsNewPath.substring(0,newFolderNamePosition);
						
						if (DbManager.checkFolderPermission(lsTargetPath, DocShellConstants.DELETE_PERMISSION)) {
							if (DbManager.removeObject(lsTargetObject, lsTargetPath)) {
								String errMsg = "FolderDocument " + lsTargetObject + " removed successfully!";
								displayError(errMsg);								
							}
							else {
								String errMsg = "Folder//Document " + lsTargetObject + " could not be removed!";
								displayError(errMsg);
							}
						}
						else
						{
							String errMsg = "No write permission in folder " + commandArray[1] + " folder, Please check path of the folder!";
							displayError(errMsg);
						}
					}
				}
				else {
					displayCommandError(DocShellConstants.INVALID_MD_ARGUMENTS);

				}
			}			else if(command.equals("ls")) {
				
				if(commandArray.length<=3) {
					QueryHandler qryHandler = new QueryHandler(getCurrentFolder());
					String params = null;
					char param = ' ';
					for (int counter=1;counter<commandArray.length;counter++) {
						params = commandArray[counter];
						if (params.substring(0,1).equals("-")) {
							for(int ctr=1;ctr<params.length();ctr++) {
								param = params.charAt(ctr);
								if (param=='l') qryHandler.setLongList(true);
							}
						}
						else
						{
							String lsNewPath = folderPathParser(params);
							qryHandler.setFolderPath(lsNewPath);
							qryHandler.setObjectPattern(params);
						}
					}
					if (null==qryHandler.getFolderPath() || qryHandler.getFolderPath().equals(DocShellConstants.EMPTY_STRING)) {
						qryHandler.setFolderPath(getCurrentFolder());
					}
					qryHandler.displayResult();
				}
				else {
					displayCommandError(DocShellConstants.INVALID_LS_ARGUMENTS);
				}
			}
			else if(command.equals("help")) {
				if (commandArray.length==1) {
					displayHelp("help");
				}
				else if(commandArray.length==2) {
					displayHelp(commandArray[1]);
				}
				else {
					displayCommandError(DocShellConstants.INVALID_HELP_ARGUMENTS);
				}
			}
			else if(command.equals("pwd")) {
				if (commandArray.length==1) {
					DisplayManager.writeLine(getCurrentFolder());
				}
				else {
					displayCommandError(DocShellConstants.INVALID_HELP_ARGUMENTS);
				}
			}	
			else {
				displayCommandError(DocShellConstants.COMMNAND_NOT_IMPLEMENTED);
			}
		}
		catch (Exception ex) {
			throw new DocShellException(DocShellException.UNKNOWN_EXCEPTION, ex.toString());
		}
	}

	private void displayHelp(String string) throws DocShellException {
		InternalCommandDetails intCmd = (InternalCommandDetails) intCommands.get(string);
		DisplayManager.writeLine(intCmd.HelpText);
		DisplayManager.writeLine(intCmd.Syntax);
	}

	public void checkCommandType(String command) {
		int liCommandType = DocShellConstants.DQL_COMMAND;
		if (intCommands.containsKey(command)==true) {
			InternalCommandDetails intCmd = (InternalCommandDetails) intCommands.get(command);
			setNumOfLines(intCmd.NumLines);
			liCommandType = DocShellConstants.INTERNAL_COMMAND;
		} 
		else if (apiCommands.containsKey(command)==true) {
			APICommandDetails apiCmd = (APICommandDetails) apiCommands.get(command);
			setNumOfLines(apiCmd.NumLines);
			DisplayManager.setPrompt(apiCmd.Prompt);
			liCommandType = DocShellConstants.API_COMMAND;
		}
		setCommandType(liCommandType);
	}

	public void displayError(String errString) throws DocShellException {
		DisplayManager.writeLine(errString);
		DisplayManager.setPrompt(getCurrentFolder());		
	}

	public void displayCommandError(String errString) throws DocShellException {
		DisplayManager.writeLine(errString);
		DisplayManager.setPrompt(getCurrentFolder());
	}

	public void setCommandType(int a_CommandType) {
		this.m_CommandType = a_CommandType;
	}

	public int getCommandType() {
		return m_CommandType;
	}

	public void setNumOfLines(int a_NumOfLines) {
		this.m_NumOfLines = a_NumOfLines;
	}
	
	public int getNumOfLines() {
		return this.m_NumOfLines;
	}

	public void resetPrompt() {
		DisplayManager.setPrompt(getCurrentFolder());
		
	}
	
	private String folderPathParser(String path) {
		String resultantPath=null;
		
		if (path.substring(0,1).equals("/")) {
			resultantPath = path;
		}
		else {
			String [] currentFolderPath = m_CurrentFolder.split("/");
			ArrayList currFolderList = new ArrayList();
			for (int counter=0;counter<currentFolderPath.length;counter++) {
				if (currentFolderPath[counter].length()>0) {
					currFolderList.add(currentFolderPath[counter]);
				}
			}		
			
			String [] relativeFolderPath = path.split("/");
			String relPath = null;
			for (int counter=0;counter<relativeFolderPath.length;counter++) {
				relPath = relativeFolderPath[counter];
				if (relPath.equals(".")) {
					// Do Nothing
				}
				else if (relPath.equals("..")) {
					if (currFolderList.size()>0) currFolderList.remove(currFolderList.size()-1);
				}
				else {
					currFolderList.add(relPath);
				}
			}
			String newPath = "";
			for (int counter=0;counter<currFolderList.size();counter++) {
				newPath = newPath + "/" + currFolderList.get(counter);
			}
			if (newPath.length()==0) {
				resultantPath="/";
			}
			else {
				resultantPath=newPath;
			}
		}
		return resultantPath;
	}

	public void appendCommand(String commandLine) {
		m_CommandLine.add(commandLine);
	}

}
